package com.geovis.boot.gen.util;

import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONArray;
import com.geovis.boot.common.dto.Result;
import com.geovis.boot.common.exception.HatchException;
import com.geovis.boot.common.util.ResultUtil;
import com.geovis.boot.common.util.ZipUtils;
import com.geovis.boot.gen.bean.Entity;
import com.geovis.boot.gen.bean.Field;
import io.swagger.annotations.ApiModelProperty;
import lombok.extern.slf4j.Slf4j;
import org.beetl.core.Configuration;
import org.beetl.core.GroupTemplate;
import org.beetl.core.Template;
import org.beetl.core.resource.ClasspathResourceLoader;

import java.io.*;
import java.sql.*;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.*;

/**
 * Mybatis-Plus 代码生成器
 * @author cl
 * @Date 211228
 */
@Slf4j
public class MPGen {

    /**
     * pg demo
     *           driver-class-name: org.postgresql.Driver
     *           url: jdbc:postgresql://127.0.0.1:5433/cj
     * */

    /**
     * mysql demo
     *           driver-class-name: com.mysql.jdbc.Driver
     *           driver-class-name: com.mysql.cj.jdbc.Driver
     *           url: jdbc:mysql://192.168.80.156:3306/seata_order?useSSL=false&useUnicode=true&characterEncoding=UTF-8
     * */

    /**
     * dm demo
     *           driver-class-name: dm.jdbc.driver.DmDriver
     *           url: jdbc:dm://192.168.80.156:5236/htzc
     * */

    //dm
    private static String driver = "dm.jdbc.driver.DmDriver";
    private static String url = "jdbc:dm://192.168.80.156:5236/htzc?characterEncoding=utf8";
    private static String username = "HTZC";
    private static String password = "qwer1234@";
    private static String gbServiceName = "";

    private static String description = "gb service";
    private static String author = "cl";

    private static final String moduleName = "/gb-gen";
    private static final String vueModuleName = "/gb-gen/vue";
    private static final String backModuleName = "/gb-gen";
    private static final String defaultPackagePrefix = "com.geovis.boot.service";

    //是否覆盖现有entity
    private static final boolean coverEntity = true;
    //是否覆盖现有mapper.xml
    private static final boolean coverMapperXml = true;
    //是否覆盖现有vo
    private static final boolean coverVO = true;
    //是否覆盖现有vue
    private static final boolean coverVue = true;
    //是否生成controller
    private static final boolean generateController = true;

    private static final String dmDriverName = "dm.jdbc.driver.DmDriver";
    private static final String pgDriverName = "org.postgresql.Driver";
    private static final String mysqlDriverName1 = "com.mysql.jdbc.Driver";
    private static final String mysqlDriverName2 = "com.mysql.cj.jdbc.Driver";

    private static final String dbDm = "dm";
    private static final String dbPg = "pg";
    private static final String dbMysql = "mysql";

    private static String dbType;

    private static final String[] coverIgnoreFiles = {
            "RegisterMapper","RegisterVipConfigMapper", "RegisterVipMemberMapper",
            "RegisterVipMemberApplicationVO", "EvidenceWebpageVO"
    };

    private static String modulePackageSuffix;
    private static String[] classNames;
    private static String modulePackage;
    private static String packagePrefix;
    private static String prefix;
    private static String prefix2;
    private static String vuePrefix;
    private static String backPrefix;
    private static String tablePre;
    private static String primaryKeyType;
    private static String version = "1.0.0";
    private static String copyRight = "中科星图";
    private static String date = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());


    private static String entityPackage = packagePrefix+".entity";
    private static String voPackage = packagePrefix+".vo";
    private static String daoPackage = packagePrefix+".mapper";
    private static String mapperPackage = packagePrefix+".mapper";
    private static String servicePackage = packagePrefix+".service";
    private static String serviceImplPackage = packagePrefix+".serviceimpl";
    private static String controllerPackage = packagePrefix+".controller";

    private static void init(String table_prefix, String genPath){
        modulePackageSuffix = table_prefix;
        modulePackage = modulePackageSuffix;
        packagePrefix = defaultPackagePrefix + (StrUtil.isBlank(gbServiceName) ? "" : "." +  gbServiceName)+"."+modulePackage;
        if(genPath == null){
            prefix = System.getProperty("user.dir")+moduleName+"/src/main/java/";
            prefix2 = System.getProperty("user.dir")+moduleName+"/src/main/resources/mapper/";
            vuePrefix = System.getProperty("user.dir")+vueModuleName+"/src/views/singleview/";
            backPrefix = System.getProperty("user.dir")+backModuleName+"/src/main/java/";
        }else{
            prefix = genPath+"/java/";
            prefix2 = genPath+"/mapper/";
            vuePrefix = genPath+"/vue/";
            backPrefix = genPath+"/java/";
        }

        entityPackage = packagePrefix+".entity";
        voPackage = packagePrefix+".vo";
        daoPackage = packagePrefix+".mapper";
        mapperPackage = packagePrefix+".mapper";
        servicePackage = packagePrefix+".service";
        serviceImplPackage = packagePrefix+".serviceimpl";
        controllerPackage = packagePrefix+".controller";
        tablePre = "";
        primaryKeyType = "Long";
    }

    //可以按照模块和表名生成
    private static void generate(String one, boolean isTable, String genPath) throws IOException {
        String moduleName = one;
        if(isTable && one.indexOf("_") != -1){
            moduleName = one.substring(0, one.indexOf("_"));
        }
        init(moduleName, genPath);
        if(isTable){
            String tableName = underline2Camel(one);
            classNames = new String[]{Character.toUpperCase(tableName.charAt(0))+tableName.substring(1)};
        }else{
            List<Map<String, String>> tables = getTablesByPrefix();
            classNames = new String[tables.size()];
            for(int i = 0; i < classNames.length; i++){
                String tableName = underline2Camel(tables.get(i).get("tableName"));
                classNames[i] = Character.toUpperCase(tableName.charAt(0))+tableName.substring(1);
            }
        }

        // 模板路径
        ClasspathResourceLoader resourceLoader = new ClasspathResourceLoader("/btl/");
        Configuration cfg = Configuration.defaultConfiguration();
        GroupTemplate gt = new GroupTemplate(resourceLoader, cfg);

        for(String oneClass: classNames){
            generateCode(gt, oneClass);// 生成代码
            //deleteCode(oneClass); // 根据类名删除生成的代码
        }
    }

    private static void deleteFiles(String one){
        init(one, null);
        List<Map<String, String>> tables = getTablesByPrefix();
        classNames = new String[tables.size()];
        for(int i = 0; i < classNames.length; i++){
            String tableName = underline2Camel(tables.get(i).get("tableName"));
            classNames[i] = Character.toUpperCase(tableName.charAt(0))+tableName.substring(1);
        }
        try{
            for(String oneClass: classNames){
                deleteCode(oneClass); // 根据类名删除生成的代码
            }
        }catch (Exception e){
            System.out.println(e);
        }
    }

    private static void generateVueInModule(String one, String tplt){
        init(one, null);
        List<Map<String, String>> tables = getTablesByPrefix();
        classNames = new String[tables.size()];
        for(int i = 0; i < classNames.length; i++){
            String tableName = underline2Camel(tables.get(i).get("tableName"));
            classNames[i] = Character.toUpperCase(tableName.charAt(0))+tableName.substring(1);
        }
        try{
            for(String oneClass: classNames){
                generateVue(oneClass, tplt);
            }
        }catch (Exception e){
            System.out.println(e);
        }

    }

    private static void generateVueOne(String oneTable, String tplt, String genPath){
        String moduleName = oneTable;
        if(oneTable.indexOf("_") != -1){
            moduleName = oneTable.substring(0, oneTable.indexOf("_"));
        }
        init(moduleName, genPath);
        String tableName = underline2Camel(oneTable);
        String className = Character.toUpperCase(tableName.charAt(0))+tableName.substring(1);
        try{
            generateVue(className, tplt);
        }catch (Exception e){
            System.out.println(e);
        }
    }

    /**
     * 生成代码
     * @param gt
     * @throws IOException
     */
    private static void generateCode(GroupTemplate gt, String className) throws IOException{
        Template entityTemplate = gt.getTemplate("entity.btl");
        Template voTemplate = gt.getTemplate("vo.btl");
        Template daoTemplate = gt.getTemplate("dao.btl");
        Template mapperTemplate = gt.getTemplate("mapper.btl");
        Template serviceTemplate = gt.getTemplate("mpService.btl");
        Template serviceImplTemplate = gt.getTemplate("mpServiceImpl.btl");
        Template controllerTemplate = gt.getTemplate("mpController.btl");
        Template mapperXmlTemplate = gt.getTemplate("mapperXml.btl");

        List<Map<String, String>> tableComments = null;

        switch (dbType){
            case dbDm:
                tableComments = getTableComment_dm(className);
                break;
            case dbPg:
                tableComments = getTableComment_pg(className);
                break;
            case dbMysql:
                tableComments = getTableComment_mysql(className);
                break;
            default: break;
        }

        if(tableComments == null || tableComments.size() < 1){
            throw new RuntimeException("表名【"+camel2Underline(className)+"】不正确");
        }

        description = tableComments.get(0).get("comments");

        Map<String, List<Map<String, String>>> towCols = null;

        switch (dbType){
            case dbDm:
                towCols = convert(getColumns_dm(className));
                break;
            case dbPg:
                towCols = convert(getColumns_pg(className));
                break;
            case dbMysql:
                towCols = convert(getColumns_mysql(className));
                break;
            default: break;
        }

        Entity entity = new Entity();
        entity.setColumns(towCols.get("forEntity"));
        entity.setColumnsAll(towCols.get("all"));
        entity.setEntityPackage(entityPackage);
        entity.setVoPackage(voPackage);
        entity.setDaoPackage(daoPackage);
        entity.setMapperPackage(mapperPackage);
        entity.setServicePackage(servicePackage);
        entity.setServiceImplPackage(serviceImplPackage);
        entity.setControllerPackage(controllerPackage);
        entity.setAuthor(author);
        entity.setClassName(className);
        entity.setTableName(tablePre+camel2Underline(className));
        entity.setClassNameLowerCase(first2LowerCase(className));
        entity.setDescription(description);
        entity.setPrimaryKeyType(primaryKeyType);
        entity.setActiviti(false);
        entity.setCopyright(copyRight);
        entity.setVersion(version);
        entity.setDate(date);

        OutputStream out = null;

        //生成实体类代码
        entityTemplate.binding("entity", entity);
        String entityResult = entityTemplate.render();
        log.info(entityResult);
        //创建文件
        String entityFileUrl = prefix+ dotToLine(entityPackage) + "/" + className + ".java";
        File entityFile = new File(entityFileUrl);
        File entityDir = entityFile.getParentFile();
        if (!entityDir.exists()) {
            entityDir.mkdirs();
        }
        if((coverEntity && coverNotIgnore(className)) || !entityFile.exists()){
            // 若文件存在则不重新生成
            entityFile.createNewFile();
            out = new FileOutputStream(entityFile);
            entityTemplate.renderTo(out);
            out.close();
        }

        //生成VO实体类代码
        voTemplate.binding("entity", entity);
        String voResult = voTemplate.render();
        log.info(voResult);
        //创建文件
        String voFileUrl = prefix+ dotToLine(voPackage) + "/" + className + "VO.java";
        File voFile = new File(voFileUrl);
        File voDir = voFile.getParentFile();
        if (!voDir.exists()) {
            voDir.mkdirs();
        }
        if((coverVO && coverNotIgnore(className + "VO")) || !voFile.exists()){
            // 若文件存在则不重新生成
            voFile.createNewFile();
            out = new FileOutputStream(voFile);
            voTemplate.renderTo(out);
            out.close();
        }

        //生成mapper代码
        mapperTemplate.binding("entity",entity);
        String mapperResult = mapperTemplate.render();
        log.info(mapperResult);
        //创建文件
        String mapperFileUrl = prefix+ dotToLine(mapperPackage) + "/" +className + "Mapper.java";
        File mapperFile = new File(mapperFileUrl);
        File mapperDir = mapperFile.getParentFile();
        if (!mapperDir.exists()) {
            mapperDir.mkdirs();
        }
        if(!mapperFile.exists()) {
            // 若文件存在则不重新生成
            mapperFile.createNewFile();
            out = new FileOutputStream(mapperFile);
            mapperTemplate.renderTo(out);
            out.close();
        }

        //生成mapperXml代码
        mapperXmlTemplate.binding("entity",entity);
        String mapperXmlResult = mapperXmlTemplate.render();
        log.info(mapperXmlResult);
        //创建文件
        String mapperXmlFileUrl = prefix2 + className + "Mapper.xml";
        File mapperXmlFile = new File(mapperXmlFileUrl);
        File mapperXmlDir = mapperXmlFile.getParentFile();
        if (!mapperXmlDir.exists()) {
            mapperXmlDir.mkdirs();
        }
        if((coverMapperXml && coverNotIgnore(className + "Mapper")) || !mapperXmlFile.exists()) {
            // 若文件存在则不重新生成
            mapperXmlFile.createNewFile();
            out = new FileOutputStream(mapperXmlFile);
            mapperXmlTemplate.renderTo(out);
            out.close();
        }

        //生成service代码
        serviceTemplate.binding("entity",entity);
        String serviceResult = serviceTemplate.render();
        log.info(serviceResult);
        //创建文件
        String serviceFileUrl = prefix+ dotToLine(servicePackage) + "/I" + className + "Service.java";
        File serviceFile = new File(serviceFileUrl);
        File serviceDir = serviceFile.getParentFile();
        if (!serviceDir.exists()) {
            serviceDir.mkdirs();
        }
        if(!serviceFile.exists()) {
            // 若文件存在则不重新生成
            serviceFile.createNewFile();
            out = new FileOutputStream(serviceFile);
            serviceTemplate.renderTo(out);
            out.close();
        }

        //生成serviceImpl代码
        serviceImplTemplate.binding("entity",entity);
        String serviceImplResult = serviceImplTemplate.render();
        log.info(serviceImplResult);
        //创建文件
        String serviceImplFileUrl = prefix+ dotToLine(serviceImplPackage) + "/I" + className + "ServiceImpl.java";
        File serviceImplFile = new File(serviceImplFileUrl);
        File serviceImplDir = serviceImplFile.getParentFile();
        if (!serviceImplDir.exists()) {
            serviceImplDir.mkdirs();
        }
        if(!serviceImplFile.exists()) {
            // 若文件存在则不重新生成
            serviceImplFile.createNewFile();
            out = new FileOutputStream(serviceImplFile);
            serviceImplTemplate.renderTo(out);
            out.close();
        }

        //生成controller代码
        //controller代码生成到bqjvip-back
        if (generateController) {
            controllerTemplate.binding("entity",entity);
            String controllerResult = controllerTemplate.render();
            log.info(controllerResult);
            //创建文件
            String controllerFileUrl = backPrefix+ dotToLine(controllerPackage) + "/" + className + "Controller.java";
            File controllerFile = new File(controllerFileUrl);
            File controllerDir = controllerFile.getParentFile();
            if (!controllerDir.exists()) {
                controllerDir.mkdirs();
            }
            if(!controllerFile.exists()) {
                // 若文件存在则不重新生成
                controllerFile.createNewFile();
                out = new FileOutputStream(controllerFile);
                controllerTemplate.renderTo(out);
                out.close();
            }
        }

        if(out!=null){
            out.close();
        }
        log.info("生成后端代码成功！");

    }

    /**
     * 删除指定类代码
     * @param className
     * @throws IOException
     */
    private static void deleteCode(String className) throws IOException{

        String entityFileUrl = prefix+ dotToLine(entityPackage) + "/" +className+".java";
        File entityFile = new File(entityFileUrl);
        if(entityFile.exists()){
            entityFile.delete();
        }
        String voFileUrl = prefix+ dotToLine(voPackage) + "/" +className+"VO.java";
        File voFile = new File(voFileUrl);
        if(voFile.exists()){
            voFile.delete();
        }
        String daoFileUrl = prefix+ dotToLine(mapperPackage) + "/" +className+"Mapper.java";
        File daoFile = new File(daoFileUrl);
        if(daoFile.exists()){
            daoFile.delete();
        }

        String serviceFileUrl = prefix+ dotToLine(servicePackage) + "/I" +className+"Service.java";
        File serviceFile = new File(serviceFileUrl);
        if(serviceFile.exists()){
            serviceFile.delete();
        }

        String serviceImplFileUrl = prefix+ dotToLine(serviceImplPackage) + "/I" +className+"ServiceImpl.java";
        File serviceImplFile = new File(serviceImplFileUrl);
        if(serviceImplFile.exists()){
            serviceImplFile.delete();
        }

        String controllerFileUrl = prefix+ dotToLine(controllerPackage) + "/" +className+"Controller.java";
        File controllerFile = new File(controllerFileUrl);
        if(controllerFile.exists()){
            controllerFile.delete();
        }

        String mapperXmlFileUrl = prefix2 + className + "Mapper.xml";
        File mapperXmlFile = new File(mapperXmlFileUrl);
        if(mapperXmlFile.exists()){
            mapperXmlFile.delete();
        }

        log.info("删除代码完毕！");
    }

    private static List<Map<String, String>> getTablesByPrefix(){
        String sql =
                "select table_name tableName, engine, table_comment tableComment, create_time createTime from information_schema.tables\n" +
                        "where table_schema = (select database()) and table_name like '"+modulePackageSuffix+"%'";
//        "select table_name tableName, engine, table_comment tableComment, create_time createTime from information_schema.tables\n" +
//                "where table_schema = (select database()) and table_name like '"+modulePackageSuffix+"_%'";
        return query(sql);
    }

    private static List<Map<String, String>> getColumns_dm(String className){
        String sql = " select  a.column_name , a.data_type , b.comments \"column_comment\"\n" +
                "    from user_tab_columns a, user_col_comments b\n" +
                "    where a.table_name = '"+tablePre+camel2Underline(className)+"' and a.table_name = b.table_name and a.column_name = b.column_name ";
        return query(sql);
    }

    /**
     * @param className
     * @return
     */
    private static List<Map<String, String>> getTableComment_dm(String className){
        String sql = "select comments from user_tab_comments where table_name = '"+tablePre+camel2Underline(className)+"';";
        return query(sql);
    }

    private static List<Map<String, String>> getColumns_pg(String className){
        //        String sql = " select  a.column_name , a.data_type , b.comments \"column_comment\"\n" +
//                "    from user_tab_columns a, user_col_comments b\n" +
//                "    where a.table_name = '"+tablePre+camel2Underline(className)+"' and a.table_name = b.table_name and a.column_name = b.column_name ";
        String sql = "select a.attname as column_name, col_description(a.attrelid, a.attnum) as column_comment, t.typname as data_type\n" +
                "from pg_class c, pg_attribute a, pg_type as t\n" +
                "where c.relname = '"+tablePre+camel2Underline(className)+"'\n" +
                "and a.atttypid = t.oid\n" +
                "and a.attrelid = c.oid\n" +
                "and a.attnum > 0";
        return query(sql);
    }

    /**
     * @param className
     * @return
     */
    private static List<Map<String, String>> getTableComment_pg(String className){
        //String sql = "select comments from user_tab_comments where table_name = '"+tablePre+camel2Underline(className)+"';";
//        String sql = "select relname, cast(obj_description(relfilenode, 'pg_class') as VARCHAR) as comments\n" +
//                "from pg_class c  where relname = "+tablePre+camel2Underline(className)+";";
        String sql = "select relname, cast(obj_description(relfilenode, 'pg_class') as VARCHAR) as comments\n" +
                "        from pg_class c where relname = '"+tablePre+camel2Underline(className)+"'";
        return query(sql);
    }


    private static List<Map<String, String>> getColumns_mysql(String className){
        String sql =
                "select column_name columnName, data_type dataType, column_comment columnComment, column_key columnKey, extra " +
                        "from information_schema.columns " +
                        "where table_name = '"+tablePre+camel2Underline(className)+"' and table_schema = (select database()) order by ordinal_position";
        //statement.execute(sql);
        return query(sql);
    }

    private static List<Map<String, String>> getTableComment_mysql(String className){
        String schemal = url.substring(url.lastIndexOf("/") + 1);
        int paramIndex = schemal.lastIndexOf("?");
        if(paramIndex != -1){
            schemal = schemal.substring(0, paramIndex);
        }
        //String sql = "SELECT table_name, table_comment as comments FROM information_schema.TABLES WHERE table_schema = 'seata_amount' and table_name = '"+tablePre+camel2Underline(className)+"';";
        String sql = "SELECT table_comment as comments FROM information_schema.TABLES WHERE table_schema = '"+schemal+"' and table_name = '"+tablePre+camel2Underline(className)+"';";
        return query(sql);
    }

    private static List<Map<String, String>> query(String sql){
        System.out.println(sql);
        Connection conn = null;
        Statement statement = null;
        ResultSet rs = null;
        List<Map<String, String>> list = null;
        try {

            Class.forName(driver);
            conn = DriverManager.getConnection(url, username, password);

            conn.setAutoCommit(false);
            conn.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);

            statement = conn.createStatement();

            rs = statement.executeQuery(sql);
//            rs.last();
//            rs.beforeFirst();
            list = new ArrayList<>();
            ResultSetMetaData rsmd = rs.getMetaData();
            int colCnt = rsmd.getColumnCount();
            while(rs.next()){
                Map<String, String> oneMap = new HashMap<>();
                for(int i = 1; i <= colCnt; i++){
                    oneMap.put(underline2Camel(rsmd.getColumnName(i)), Optional.ofNullable(rs.getObject(i)).map(obj -> obj.toString()).orElse(""));
                }
                list.add(oneMap);
            }
            return list;
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
            log.error(e.getMessage());
            throw new RuntimeException("驱动错误");
        }catch (SQLException e){
            e.printStackTrace();
            log.error(e.getMessage());
            throw new RuntimeException("请检查数据库地址和用户名密码");
        }finally {
            if(rs != null){
                try {
                    rs.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if(statement != null){
                try {
                    statement.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if(conn != null){
                try {
                    conn.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    private static void sqlExecute(String sql){
        System.out.println(sql);
        Connection conn = null;
        Statement statement = null;
        ResultSet rs = null;
        try {

            Class.forName(driver);
            conn = DriverManager.getConnection(url, username, password);

            conn.setAutoCommit(false);
            conn.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);

            statement = conn.createStatement();

            statement.execute(sql);

        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            if(rs != null){
                try {
                    rs.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if(statement != null){
                try {
                    statement.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if(conn != null){
                try {
                    conn.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    private static Map<String, String> convertTypes(){
        Map<String, String> types = new HashMap<>();
        types.put("tinyint", "Integer");
        types.put("int2", "Integer");
        types.put("int4", "Integer");
        types.put("int8", "Integer");
        types.put("smallint", "Integer");
        types.put("mediumint", "Integer");
        types.put("int", "Integer");
        types.put("integer", "Integer");
        types.put("bigint", "Long");
        types.put("float", "Float");
        types.put("double", "Double");
        types.put("numeric", "Double");
        types.put("float8", "Double");
        types.put("decimal", "BigDecimal");
        types.put("bit", "Boolean");
        types.put("char", "String");
        types.put("varchar", "String");
        types.put("bpchar", "String");
        types.put("tinytext", "String");
        types.put("text", "String");
        types.put("mediumtext", "String");
        types.put("longtext", "String");
        types.put("enum", "String");
        types.put("date", "Date");
        types.put("datetime", "Date");
        types.put("timestamp", "Date");
        return types;
    }

    private static Map<String, List<Map<String, String>>> convert(List<Map<String, String>> columns){
        Map<String, List<Map<String, String>>> towCols = new HashMap<>();
        List<String> excludeColumns = Arrays.asList(new String[]
                {"id", "createBy", "createTime", "updateBy", "updateTime", "delFlag"});
        Map<String, String> types = convertTypes();
        List<Map<String, String>> columnsAll = new ArrayList<>();
        for(int i = 0; i < columns.size(); i++){
            Map<String, String> one = columns.get(i);
            one.put("dbDataType", one.get("dataType").toLowerCase());
            one.put("dataType", types.get(one.get("dataType").toLowerCase()));
            one.put("columnNameUnderline", one.get("columnName"));
            one.put("columnName", underline2Camel(one.get("columnName")));
            //处理备注中的双引号和回车
            if(one.get("columnComment")!=null){
                one.put("columnComment", one.get("columnComment").replaceAll("\"|\n|\r", " "));
            }
            columnsAll.add(one);
            for(String oneExclude:excludeColumns){
                if(oneExclude.equals(one.get("columnName"))){
                    columns.remove(one);
                    i--;
                }
            }
        }
        towCols.put("all", columnsAll);
        towCols.put("forEntity", columns);
        return towCols;
    }


    /**
     * 下划线转驼峰法
     */
    private static String underline2Camel(String str) {
        str = str.toLowerCase();
        if (StrUtil.isBlank(str)) {
            return "";
        }
        if(str.length()==1){
            return str.toLowerCase();
        }
        StringBuffer sb = new StringBuffer();
        for(int i=1;i<str.length();i++){
            if(str.charAt(i) == '_'){
                sb.append(Character.toUpperCase(str.charAt(++i)));
            }else{
                sb.append(str.charAt(i));
            }
        }
        return (str.charAt(0)+sb.toString());
    }

    private static String gengerateEntityData(String className) throws Exception {
        Class c = Class.forName(className);
        Object obj = c.newInstance();

        String start = "[";
        String end = "\n]";
        String field_all = "";
        java.lang.reflect.Field[] fields = obj.getClass().getDeclaredFields();
        for (int j = 0; j < fields.length; j++) {
            java.lang.reflect.Field field = fields[j];
            field.setAccessible(true);
            // 字段名
            String fieldName = field.getName();
            String fieldType = field.getType().getName();
            // 白名单
            if("serialVersionUID".equals(fieldName)||"actBusinessId".equals(fieldName)||"applyUser".equals(fieldName)
                    ||"routeName".equals(fieldName)||"procInstId".equals(fieldName)||"applyTime".equals(fieldName)
                    ||"status".equals(fieldName)||"result".equals(fieldName)){
                continue;
            }

            // 获得字段注解
            ApiModelProperty myFieldAnnotation = field.getAnnotation(ApiModelProperty.class);
            String fieldName_CN = fieldName;
            if (myFieldAnnotation != null) {
                fieldName_CN = myFieldAnnotation.value();
            }
            fieldName_CN = (fieldName_CN == null || fieldName_CN == "") ? fieldName : fieldName_CN;

            String type = "text";
            String searchType = "text";
            // 日期字段特殊处理,其他一律按 字符串界面处理
            if (fieldType == "java.lang.Date" || fieldType == "java.util.Date" || fieldType == "Date") {
                type = "date";
                searchType = "daterange";
            }
            String field_json = "\n        {\n" +
                    "            \"sortOrder\": " + j + ",\n" +
                    "            \"field\": \"" + fieldName + "\",\n" +
                    "            \"name\": \"" + fieldName_CN + "\",\n" +
                    "            \"level\": \"2\",\n" +
                    "            \"tableShow\": true,\n" +
                    "            \"editable\": true,\n" +
                    "            \"type\": \"" + type + "\",\n" +
                    "            \"searchType\": \"" + searchType + "\",\n" +
                    "            \"searchLevel\": \"2\",\n" +
                    "            \"validate\": false,\n" +
                    "            \"searchable\": true,\n" +
                    "            \"sortable\": false,\n" +
                    "            \"defaultSort\": false,\n" +
                    "            \"defaultSortType\": \"desc\"\n" +
                    "        }";
            String splitChar = field_all == "" ? "" : ",";
            field_all = field_all + splitChar + field_json;
        }
        String json = start + field_all + end;
        return json;
    }

    private static String gengerateEntityDataByDb(String className) throws Exception {

        Map<String, List<Map<String, String>>> towCols = null;

        switch (dbType){
            case dbDm:
                towCols = convert(getColumns_dm(className));
                break;
            case dbPg:
                towCols = convert(getColumns_pg(className));
                break;
            case dbMysql:
                towCols = convert(getColumns_mysql(className));
                break;
            default: break;
        }

//        if(towCols == null || towCols.size() < 1){
//            throw new RuntimeException("表名【"+camel2Underline(className)+"】不正确");
//        }
        List<Map<String, String>> cols = towCols.get("all");

        String start = "[";
        String end = "\n]";
        String field_all = "";
        //java.lang.reflect.Field[] fields = obj.getClass().getDeclaredFields();
        int sortOrder = 0;
        for(Map<String, String> col:cols){

            // 字段名
            String fieldName = col.get("columnName");
            String fieldNameCN = col.get("columnComment");
            String fieldType = col.get("dataType");
            // 白名单
            if("serialVersionUID".equals(fieldName)||"actBusinessId".equals(fieldName)||"applyUser".equals(fieldName)
                    ||"routeName".equals(fieldName)||"procInstId".equals(fieldName)||"applyTime".equals(fieldName)
                    ||"status".equals(fieldName)||"result".equals(fieldName)){
                continue;
            }

            String type = "text";
            String searchType = "text";
            // 日期字段特殊处理,其他一律按 字符串界面处理
            if (fieldType == "java.lang.Date" || fieldType == "java.util.Date" || fieldType == "Date") {
                type = "date";
                searchType = "daterange";
            }
            String field_json = "\n        {\n" +
                    "            \"sortOrder\": " + ++sortOrder + ",\n" +
                    "            \"field\": \"" + fieldName + "\",\n" +
                    "            \"name\": \"" + fieldNameCN + "\",\n" +
                    "            \"level\": \"2\",\n" +
                    "            \"tableShow\": true,\n" +
                    "            \"editable\": true,\n" +
                    "            \"type\": \"" + type + "\",\n" +
                    "            \"searchType\": \"" + searchType + "\",\n" +
                    "            \"searchLevel\": \"2\",\n" +
                    "            \"validate\": false,\n" +
                    "            \"searchable\": true,\n" +
                    "            \"sortable\": false,\n" +
                    "            \"defaultSort\": false,\n" +
                    "            \"defaultSortType\": \"desc\"\n" +
                    "        }";
            String splitChar = field_all == "" ? "" : ",";
            field_all = field_all + splitChar + field_json;
        }
        String json = start + field_all + end;
        return json;
    }

    private static String generateVueFile(String template, String vueName, Integer rowNum, List<Field> fields) throws IOException {

        // 模板路径
        ClasspathResourceLoader resourceLoader = new ClasspathResourceLoader("/btl/vue/");
        Configuration cfg = Configuration.defaultConfiguration();
        GroupTemplate gt = new GroupTemplate(resourceLoader, cfg);

        Template tableTemplate = gt.getTemplate(template+".btl");
        // 排序
        Collections.sort(fields, Comparator.comparing(Field::getSortOrder));
        // 绑定变量
        tableTemplate.binding("vueName", vueName);
        tableTemplate.binding("fields", fields);
        // 判断有无upload和日期范围搜索
        Boolean upload = false;
        for(Field f:fields){
            if("upload".equals(f.getType())){
                upload = true;
            }
        }
        tableTemplate.binding("upload", upload);
        if("table".equals(template)){
            // 判断有无upload和日期范围搜索
            Boolean daterangeSearch = false;
            for(Field f:fields){
                if(f.getSearchable()&&"daterange".equals(f.getSearchType())){
                    daterangeSearch = true;
                }
            }
            tableTemplate.binding("daterangeSearch", daterangeSearch);
            // 统计搜索栏个数 判断是否隐藏搜索栏
            Boolean hideSearch = false;
            List<Field> firstTwo = new ArrayList<>();
            List<Field> rest = new ArrayList<>();
            Integer count = 0;
            for(Field f:fields){
                if(f.getSearchable()){
                    count++;
                    if(count<=2){
                        firstTwo.add(f);
                    }else{
                        rest.add(f);
                    }
                }
            }
            if(count>=4){
                hideSearch = true;
                tableTemplate.binding("firstTwo", firstTwo);
                tableTemplate.binding("rest", rest);
            }
            tableTemplate.binding("searchSize", count);
            tableTemplate.binding("hideSearch", hideSearch);
            // 获取默认排序字段
            String defaultSort = "", defaultSortType = "";
            for(Field f:fields){
                if(f.getDefaultSort()){
                    defaultSort = f.getField();
                    defaultSortType = f.getDefaultSortType();
                    break;
                }
            }
            tableTemplate.binding("defaultSort", defaultSort);
            tableTemplate.binding("defaultSortType", defaultSortType);
        }
        // 一行几列
        tableTemplate.binding("rowNum", rowNum);
        if(rowNum==1){
            tableTemplate.binding("modalWidth", 500);
            tableTemplate.binding("width", "100%");
            tableTemplate.binding("editWidth", "100%");
            tableTemplate.binding("itemWidth", "");
            tableTemplate.binding("span", "9");
        }else if(rowNum==2){
            tableTemplate.binding("modalWidth", 770);
            tableTemplate.binding("width", "250px");
            tableTemplate.binding("editWidth", "250px");
            tableTemplate.binding("itemWidth", "350px");
            tableTemplate.binding("span", "17");
        }else if(rowNum==3){
            tableTemplate.binding("modalWidth", 980);
            tableTemplate.binding("width", "200px");
            tableTemplate.binding("editWidth", "200px");
            tableTemplate.binding("itemWidth", "300px");
            tableTemplate.binding("span", "17");
        }else if(rowNum==4){
            tableTemplate.binding("modalWidth", 1130);
            tableTemplate.binding("width", "160px");
            tableTemplate.binding("editWidth", "160px");
            tableTemplate.binding("itemWidth", "260px");
            tableTemplate.binding("span", "17");
        }else{
            throw new HatchException("rowNum仅支持数字1-4");
        }
        // 生成代码
        String result = tableTemplate.render();
        log.info(result);
        Writer out = null;
        //创建文件
        String vueFileUrl = vuePrefix + vueName + ".vue";
        File vueFile = new File(vueFileUrl);
        File vueDir = vueFile.getParentFile();
        if (!vueDir.exists()) {
            vueDir.mkdirs();
        }
        if(!vueFile.exists() || coverVue) {
            // 若文件存在则不重新生成
            vueFile.createNewFile();
            out = new FileWriter(vueFile);
            out.write(result);
            out.flush();
        }

        if(out!=null){
            out.close();
        }
        log.info("生成vue代码 ["+vueFileUrl+"] 成功！");
        return vueFileUrl;
    }

    /**
     * 生成.vue文件
     * */
    private static void generateVue(String className, String tplt) {
        try {
            //通过对象生成
            //String fields = gengerateEntityData(voPackage+"."+className+"VO");
            //通过
            String fields = gengerateEntityDataByDb(camel2Underline(className));
            List<Field> fieldList = JSONArray.parseArray(fields, Field.class);
            String genedPath = generateVueFile(tplt, className, 1, fieldList);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private static boolean coverNotIgnore(String fileName){
        List<String> ignoredFiles = Arrays.asList(coverIgnoreFiles);
        for(String one:ignoredFiles){
            if(one.equalsIgnoreCase(fileName)){
                return false;
            }
        }
        return true;
    }

    /**
     * 驼峰法转下划线
     */
    private static String camel2Underline(String str) {
        if (StrUtil.isBlank(str)) {
            return "";
        }
        if(str.length()==1){
            return str.toLowerCase();
        }
        StringBuffer sb = new StringBuffer();
        for(int i=1;i<str.length();i++){
            if(Character.isUpperCase(str.charAt(i))){
                sb.append("_"+Character.toLowerCase(str.charAt(i)));
            }else{
                sb.append(str.charAt(i));
            }
        }
        return (str.charAt(0)+sb.toString()).toLowerCase();
    }

    /**
     * 首字母小写
     */
    private static String first2LowerCase(String str) {
        if (StrUtil.isBlank(str)) {
            return "";
        }
        if(str.length()==1){
            return str.toLowerCase();
        }
        StringBuffer sb = new StringBuffer();
        sb.append(Character.toLowerCase(str.charAt(0)));
        sb.append(str.substring(1,str.length()));
        return sb.toString();
    }

    private static String dotToLine(String str){
        return str.replace(".", "/");
    }

    public static Result gen(MpGenConfig mpGenConfig, String[] tables, String genPath) throws Exception {

        if(StrUtil.isBlank(mpGenConfig.getDriver())){
            return ResultUtil.error("驱动名称不能为空");
        }
        if(StrUtil.isBlank(mpGenConfig.getUrl())){
            return ResultUtil.error("数据库连接不能为空");
        }
        if(StrUtil.isBlank(mpGenConfig.getUsername())){
            return ResultUtil.error("数据库用户名不能为空");
        }
        if(StrUtil.isBlank(mpGenConfig.getPassword())){
            return ResultUtil.error("数据库密码不能为空");
        }
        if(StrUtil.isBlank(mpGenConfig.getGbServiceName())){
            return ResultUtil.error("服务名称不能为空");
        }

        driver = mpGenConfig.getDriver();
        url = mpGenConfig.getUrl();
        username = mpGenConfig.getUsername();
        password = mpGenConfig.getPassword();
        gbServiceName = mpGenConfig.getGbServiceName();

        author = StrUtil.isNotBlank(mpGenConfig.getAuthor()) ? mpGenConfig.getAuthor() : author;
        version = StrUtil.isNotBlank(mpGenConfig.getVersion()) ? mpGenConfig.getVersion() : version;
        copyRight = StrUtil.isNotBlank(mpGenConfig.getCopyRight()) ? mpGenConfig.getCopyRight() : copyRight;

        //return ResultUtil.data("preInit done");

        getDbType();
        if(StrUtil.isBlank(dbType)){
            return ResultUtil.error("未知数据库类型");
        }

        for(String one:tables){
            //生成后端代码
            generate(one.trim(), true, genPath);
            //生成前端代码
            generateVueOne(one, "table", genPath);
        }
        if(genPath != null){
            ZipUtils.zip(genPath, genPath+".zip");
            return ResultUtil.data(genPath+".zip");
        }
        return ResultUtil.data("本地执行，生成目录详见日志输出");
    }

    /**
     * @return 数据库类型，dm，pg，mysql
     */
    private static void getDbType(){
        if(driver.equalsIgnoreCase(dmDriverName)){
            dbType = dbDm;
            return;
        }
        if(driver.equalsIgnoreCase(pgDriverName)){
            dbType =  dbPg;
            return;
        }
        if(driver.equalsIgnoreCase(mysqlDriverName1) || driver.equalsIgnoreCase(mysqlDriverName2)){
            dbType =  dbMysql;
            return;
        }
        else {
            dbType = "";
        }
    }

    /**
     * 运行该主函数即可生成代码
     * @param args
     * @throws IOException
     */
    private static void main(String[] args) throws Exception {

        String[] tables = {/*"st_site", "st_site", "st_task_site""st_judge_request",*/ "file_bind"};

        MpGenConfig mpGenConfig = new MpGenConfig();
        mpGenConfig.setDriver("dm.jdbc.driver.DmDriver");
        mpGenConfig.setUrl("jdbc:dm://192.168.80.156:5236/htzc?characterEncoding=utf8");
        mpGenConfig.setUsername("HTZC");
        mpGenConfig.setPassword("qwer1234@");
        mpGenConfig.setGbServiceName("demo");

        gen(mpGenConfig, tables, null);
    }

}
